/*
 * Includes
 */
#include <stdio.h>
#include <stdlib.h>
#include <minix/drivers.h>
#include <minix/driver.h>
#include <minix/safecopies.h>
#include "com.h"
#include "alphabet.c"

#define DEV_NAME "com"
#define OK 0

int cl_endpoint, cs_endpoint;
/*
 * Device functions
 */
FORWARD _PROTOTYPE(char *com_name, (void));
FORWARD _PROTOTYPE(int com_open, (struct driver *d, message *m));
FORWARD _PROTOTYPE(int com_close, (struct driver *d, message *m));
FORWARD _PROTOTYPE(int do_receive_message, (struct driver *d, message *m));
FORWARD _PROTOTYPE(struct device *com_prepare, (int d));
FORWARD _PROTOTYPE(int com_transfer,
		(endpoint_t endpoint, int opcode, u64_t position, iovec_t *vector,
				unsigned nr_req, endpoint_t UNUSED( user_endpoint),
				unsigned int UNUSED( flags)));
FORWARD _PROTOTYPE(void com_geometry, (struct partition *entry));

/*
 * SEF prototypes
 */
FORWARD _PROTOTYPE(void sef_local_startup, (void));
FORWARD _PROTOTYPE(int sef_cb_init, (int type, sef_init_info_t *info));

/*
 * Device entrypoint
 */
static struct driver com_tab = { com_name, com_open, com_close, nop_ioctl,
		com_prepare, com_transfer, nop_cleanup, com_geometry, nop_alarm,
		nop_cancel, nop_select, /*do_receive_message,*/
		nop_ioctl,
		do_nop, };

PRIVATE int do_receive_message(struct driver *d, message *m) {

	printf("receiving messages in com driver\n");

	return OK;
}

/*
 * Represents the /dev/com device
 */
PRIVATE struct device com_device;

/*
 * Returns the device name
 */
PRIVATE char *com_name() {
	return DEV_NAME;
}

/*
 * Opens the device
 */
PRIVATE int com_open(struct driver *d, message *m) {
	return OK;
}

/*
 * Close the device
 */
PRIVATE int com_close(struct driver *d, message *m) {
	printf("closing the comm driver");
	return OK;
}

/*
 * Prepares the device
 */
PRIVATE struct device *com_prepare(int d) {
	com_device.dv_base.lo = 0;
	com_device.dv_base.hi = 0;
	com_device.dv_size.lo = 0;
	com_device.dv_size.hi = 0;

	return &com_device;
}

/*
 * Performs the read/write of the device
 */
PRIVATE int com_transfer(endpoint_t endpoint, int opcode, u64_t position,
		iovec_t *vector, unsigned nr_req, endpoint_t UNUSED( user_endpoint),
		unsigned int UNUSED( flags)) {
	/* Variables */
	int bytes, ret;
	char buffer[32];

	bytes = strlen(buffer) > vector->iov_size
			? strlen(buffer)
			: vector->iov_size;

	if (bytes <= 0) {
		return OK;
	} else {
		switch (opcode) {
		case DEV_GATHER_S: printf("Entered DEV_GATHER_S\n"); break;
		case DEV_SCATTER_S: {
			/* Received data */
			printf("Entered DEV_SCATTER_S from COM\n");
			ret = sys_safecopyfrom(endpoint, vector->iov_addr, 0, (vir_bytes)(buffer), bytes, D);
		} break;
		default:
			return EINVAL;
			break;
		}
	}
	return ret;
}

PRIVATE void com_geometry(struct partition *entry) {
	entry->cylinders = 0;
	entry->heads = 0;
	entry->sectors = 0;
}

void hexdump(const char* buf, const size_t size) {
	unsigned addr;
	int i;
	unsigned char* p;

	for (addr = 0; addr < size; addr += 16) {
		p = (unsigned char*) &buf[addr];
		printf("%04X: ", addr);
		i = size - addr;
		if (i > 16)
			i = 16;
		while (i--)
			printf("%02X ", *p++);
		printf("\n");
	}
}

/*
 * Runs the program
 */
PUBLIC int main(int argc, char* argv[]) {
	message com_mess; /* buffer for all incoming messages */
	int r, i;
	int sendreply;
	int endpoints[NO_RT_DEVICES];
	struct queue actionList[NO_OF_ACTIONS];

	/* Initialize variables */
	i = 0;
	endpoints[0] = 0;/*cs*/
	endpoints[1] = 0;/*com*/
	endpoints[2] = 0;/*cl*/
	initializeAlphabetList(alphabetList);
	initializeactionList(&actionList[0]);

	/* SEF local startup. */
	sef_local_startup();

	while (TRUE) {
		r = sef_receive(ANY, &com_mess);
		if (r != 0)
			panic("driver_receive failed with: %d", r);

		switch (com_mess.m_type) {
		case SEND_ENDPOINT: {
			/*Only send reply if message is received from pm and not directly*/
			sendreply = com_mess.m_source == PM_PROC_NR
											? TRUE
											: FALSE;
			/*If send from pm the save the msg endpoint*/
			endpoints[com_mess.RT_DRIVER_ID] = com_mess.m_source == PM_PROC_NR
																	? com_mess.RT_DRIVER_ENDPT
																	: com_mess.m_source;

			/* Reply to initializing driver, make endpoint known */
			if (sendreply) {
				int s;
				message m;

				/* Set message properties and announce own endpoint */
				m.m_type = SEND_ENDPOINT;
				m.RT_DRIVER_ID = RT_DEV_COM;
				m.RT_DRIVER_ENDPT = 0;
				if (OK != (s = send(endpoints[com_mess.RT_DRIVER_ID], &m))) {
					panic("Did not send endpoint from COM to CS.\n");
				}
			}
		} break;
		case CTRL_TO_COM: printf("Value from COM:%d\n", com_mess.RS232_CTRL); break;
		case INIT_PROC: {
			message m;
			int s, r;
			char *ptr, actions[50], *argument, *arguments[10];
			struct alphabet alphabet;
			int argc = 0;

			ptr = com_mess.PROC_ACTIONS;
			/* Copy data to own process by means of pointer */
			r = sys_datacopy(com_mess.m_source, (vir_bytes)ptr, SELF, (vir_bytes)actions, sizeof(actions));
			if (r != OK)
				panic("sys_datacopy failed: %d", r);

			/* Loop through the action string*/
			argument = strtok(actions, " ");
			while (argument != NULL) {
				arguments[argc] = argument;
				argument = strtok(NULL, " ");
				argc++;
			}

			/* Set alphabet properties */
			alphabet.pid = com_mess.RT_PID;
			alphabet.actions = arguments;

			/* Pass the alphabet to its handler */
			insertAlphabet(alphabetList, alphabet);
			insertProcesList(&actionList[0],alphabet, com_mess.m_source);

			/* Reply to process */
			m.m_type = INIT_PROC;
			if (OK != (s = send(com_mess.m_source, &m))) {
				panic("Did not send to process.\n");
			}

		} break;
		case ASK_FOR_GO: {
			char action[50], *ptr;
			int r, go;

			ptr = com_mess.PROC_ACTIONS;
			/* Copy data to own process by means of pointer */
			r = sys_datacopy(com_mess.m_source, (vir_bytes)ptr, SELF, (vir_bytes)action, sizeof(action));
			if (r != OK)
				panic("sys_datacopy failed: %d", r);

			/* Check if action is ready to go */
			go = askForGo(&actionList[0],action, com_mess.RT_PID, endpoints[RT_DEV_CS]);
				printf("Ask for go result: %d\n", go);
		} break;
		default: printf("Warning, COM driver got unexpected request: %d\n", com_mess.m_type); break;
		}
	}

	/*driver_task(&com_tab, DRIVER_STD);*/

	/* Run the main loop */
	/**/
	return OK;
}

/*
 * Sets the SEF calls
 */
PRIVATE void sef_local_startup() {
	/* Register init call backs */
	sef_setcb_init_fresh(sef_cb_init);
	sef_setcb_init_lu(sef_cb_init);
	sef_setcb_init_restart(sef_cb_init);

	/* Register live update call backs. */
	sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
	sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);

	/* Startup SEF */
	sef_startup();
}

/*
 * Handles all the SEF calls
 */
PRIVATE int sef_cb_init(int type, sef_init_info_t *info) {
	int do_announce_driver = TRUE;

	switch (type) {
	case SEF_INIT_FRESH: {
		/* Initialise the driver */

		int s;
		message m;
		m.m_type = PM_DRIVER_INIT;
		m.RT_DRIVER_ID = RT_DEV_COM;

		/* Announce endpoint to PM */
		if (OK != (s = send(PM_PROC_NR, &m))) {
			panic("Driver init msg did not send\n");
		}
		printf("%s: driver is initialized.\n", PROGRAM_NAME);


	}
		break;

	case SEF_INIT_LU:
		printf("%s: driver is updated.\n", PROGRAM_NAME);
		do_announce_driver = FALSE;
		break;

	case SEF_INIT_RESTART:
		printf("%s: driver is restarted.\n", PROGRAM_NAME);
		break;
	}

	if (do_announce_driver) {
		driver_announce();
	}

	/* Driver initialization successful */
	return OK;
}
